/*
 * ExplicaModuleView.java
 *
 * $Id: ExplicaModuleView.java,v 1.9 2009-10-27 10:09:06 mario Exp $
 */
package org.ceteca.explica.client.gui;

import org.apache.log4j.Logger;
import org.ceteca.explica.client.ClientContext;
import org.ceteca.explica.core.InterfaceExplica;
import org.ceteca.explica.core.installer.EntityLauncher;
import org.ceteca.explica.core.installer.EntityLauncherFactory;
import org.ceteca.explica.core.installer.EntityOT;
import org.ceteca.explica.core.installer.ModuleOT;
import org.ceteca.javaglade.Gui;
import org.ceteca.javaglade.InterfaceGladeGtk;
import org.ceteca.javaglade.handlers.GenericHandler;
import org.ceteca.javaglade.toolkit.swt.SwtWidget;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;

/**
 * Helper class that implements the Explica module GUI. 
 * It completes the glade file specification to add extra features.
 * It also implements the event handlers for the Explica module buttons.
 * <br/>
 * Responsibilities:
 * <ul>
 * 	<li>Definition and inicialization of the Explica module GUI. </li>
 *  <li>Event management for the Explica module widgets.</li>
 * </ul>
 * <br/>
 * @author Mario García García <mario@imagos.es>
 * Copyright (c) 2008 Fundación Centro Tecnolóxico da Carne
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <br/>
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * <br/>
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class ExplicaModuleView extends GenericHandler {
	/** Instance of the logging system for the module */
	private Logger logger;
	
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * loading of the entity's helper class.
	 */
	public static final String ERROR_ENTITY_NOHELPERCLASS = "Cannot find the entity's helper class!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * instantiation of the entity's helper class.
	 */
	public static final String ERROR_ENTITY_NOHELPERINSTANCE = "An instance of the entity's helper class couldn't be created!";
	
	/** Unique instance of the ExplicaModuleView class. Singleton pattern */
	private static ExplicaModuleView instance;
	
	/** Instance of the core view where the module view will be embedded */
	private Gui gui;

	/**
	 * Returns the unique instance of the ExplicaModuleView class. Implements
	 * the singleton dessign pattern.
	 * 
	 * @return ExplicaModuleView, unique instance of ExplicaModuleView.
	 */
	public static final ExplicaModuleView getInstance() {
		if (instance == null)
			instance = new ExplicaModuleView();
		return instance;
	}
	
	private ExplicaModuleView() {
		this.logger = ClientContext.getInstance().getLogger();
		this.gui = ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME);
		this.buildModuleView();
	}
	
	/**
	 * Initializes the Explica Module Viewer engine and builds
	 * the module panel. Searches automatically for the glade interface 
	 * file with the module GUI configuration.
	 */
	private void buildModuleView() {
		// Get the entity contents panel
		SwtWidget parentPanel = this.getModuleContentsPanel();
		Composite entityPanel = (Composite)parentPanel.getWidget();
		this.logger.debug("Entity content panel found: " + entityPanel);
		
		final StackLayout stackLayout = new StackLayout();
		entityPanel.setLayout(stackLayout);
//		entityPanel.setBackground(ExplicaGUIBuilder.getInstance().getColor(SWT.COLOR_DARK_RED));
		
		// Build an empty panel to show as the default panel of the stack layout
		Widget w = ExplicaGUIBuilder.getInstance().getToolkit().emptyPanel(entityPanel, InterfaceExplica.EXPLICA_ENTITY_EMPTY_PANEL_NAME);
		((Control)w).setLayoutData(null);
//		((Control)w).setBackground(ExplicaGUIBuilder.getInstance().getColor(SWT.COLOR_BLUE));
		this.logger.debug("Empty entity panel created for showing when no entity has been selected yet");
		this.gui.addWidget(InterfaceExplica.EXPLICA_ENTITY_EMPTY_PANEL_NAME, w, parentPanel.getWidgetName());		
		
		// Set up the empty panel as the showed control 
		stackLayout.topControl = (Control)w;
	}

	/**
	 * Builds the new button for launching the entity. 
	 * @param entOT EntityOT, data of the entity the button is built for.
	 * @param toolbar ToolBar, toolbar widget where the buttons will be placed.
	 */
	private void addEntity(EntityOT entOT, SwtWidget parentToolbar) {
		if (entOT != null) {
			ToolBar toolbar = (ToolBar)parentToolbar.getWidget();
			// Build the button that will launch the entity
			ToolItem buttonEnt = (ToolItem)ExplicaGUIBuilder.getInstance().getToolkit().
				toolButton(toolbar, "EXPLICA_ENTITY_" + entOT.getName() + "_BUTTON", ClientContext.getInstance().getI18n().tr(entOT.getName()), "icon.gif");
			buttonEnt.setEnabled(true);
			this.logger.debug("Entity button built: " + buttonEnt);
			this.gui.addWidget(
					InterfaceExplica.EXPLICA_ENTITY_BUTTON_NAME + "_" + entOT.getName(), 
					buttonEnt,
					parentToolbar.getWidgetName());

			// Get the entity launcher factory for creating a new entity launcher
			// that will handle the entity events.
			EntityLauncherFactory factory = this.getEntityLauncherFactory(entOT);

			// Get the entity launcher instance that will handle the entity events
			EntityLauncher entLauncher = factory.getEntityLauncher(this.getModuleContentsPanel());
			entLauncher.initEntityLauncher(this.logger, this.gui, this.getModuleContentsPanel());
			
			this.logger.debug("Entity helper class found: " + entLauncher);
			
			// Update the catalog of entities with the new created entity button
			this.logger.info("Añadiendo entidad al catalogo: " + entOT.getName() + " - " + buttonEnt + " - " + entLauncher);
			buttonEnt.setData(entLauncher);
						
			this.logger.debug("Connecting entity button clicked signal to its handler!");
			// Connect the signal to the button
			ExplicaGUIBuilder.getInstance().connectSignal(InterfaceGladeGtk.GTK_SIGNAL_TYPE_CLICKED, buttonEnt, 
					InterfaceExplica.EXPLICA_ENTITY_BUTTON_CLICKED_HANDLER, this);
		}
		else {
			this.logger.error(InterfaceExplica.EXPLICA_LOG_ERROR_GETTING_ENTITY);	
		}
	}

	/**
	 * Handler for the entity button click event.
	 * Inits the entity and shows the entity main window on the screen.
	 * @param e Event, entity button clicked event data.
	 */
	public void on_entityButton_clicked(Event e) {
		// Get the button widget that raised the event
		ToolItem entButton = (ToolItem)e.widget;
		this.logger.info("Entity button clicked: " + entButton.getText());
		
		// Get the entity launcher corresponding to the pressed button module
		EntityLauncher entLauncher = (EntityLauncher)entButton.getData();

		// Get the entity data corresponding to the pressed button module		
		EntityOT entOT = entLauncher.getEntity();
		
		this.logger.debug("Entity launcher located: " + entLauncher);
		
		// Start the entity
		if (entLauncher != null && entOT != null) {
			this.logger.debug("Starting entity: " + entButton.getText());
			entLauncher.buildEntity();
		}
		else {
			this.logger.error(String.format(InterfaceExplica.EXPLICA_LOG_ENTITY_NOT_FOUND, entButton.getText()));
		}
		
		this.logger.info("--------  JAVAGLADE STRUCTURE -----------");
		ExplicaGUIBuilder.getInstance().printGUIs(this.logger);
		this.logger.info("--------  JAVAGLADE STRUCTURE -----------");
//		ExplicaGUIBuilder.getInstance().printWidgets(InterfaceExplica.EXPLICA_CORE_GLADE_NAME, this.logger);
	}
	
	/**
	 * Build and show the module data on the screen. It builds the module
	 * panel if it's the first time the module is accessed. Fills the module
	 * panel with the available entity buttons. Show the module panel
	 * to the user.
	 */
	public void showModule(ModuleOT moduleData, Logger logger) {
		this.logger = logger;
		
		// Clear the contents of the previous module is any was shown
		this.clearModule();
		
		// Build the contents of the new showed module
		this.buildModule(moduleData);
		
//		ExplicaGUIBuilder.getInstance().printWidgets();
	}

	/**
	 * Clear the module contents and show an empty module.
	 */
	private void clearModule() {
		// Get the toolbar to fill it with all the entities defined for the module
		SwtWidget toolbarWidget = this.getEntitiesToolBar();
		ToolBar toolBarEntities = (ToolBar)toolbarWidget.getWidget();
	
		// Clear the entity buttons from the toolbar
		ToolItem[] lButtons = toolBarEntities.getItems();
		for (int i=0; lButtons != null && i<lButtons.length; i++)
			lButtons[i].dispose();
		
		// Get the module panel
		SwtWidget parentWidget = this.getModuleContentsPanel(); 
		Composite modulePanel = (Composite)parentWidget.getWidget();
		
		// Set the content of the module panel as the empty panel
		for (Control c : modulePanel.getChildren())
			c.dispose();
		
	}
	
	/**
	 * Fills the module entities toolbar and the module contents
	 * panel with the data of the module.
	 */
	private void buildModule(ModuleOT moduleData) {
		// Get the entities toolbar where the entity buttons will be placed
		SwtWidget toolBarEntities = this.getEntitiesToolBar();
		
		// Fill the toolbar with the buttons for launching the defined entities
		if (moduleData.getLEntities() == null || moduleData.getLEntities().size() <= 0) {
			this.logger.error(String.format("ERROR! NO ENTITIES HAVE BEEN DEFINED FOR THE MODULE %s!", moduleData.getName()));
		} // if no entities defined
		else {
			for (EntityOT ent : moduleData.getLEntities()) {
				if (ent.isVisible()) {
					this.logger.info(String.format("Module %s --> Adding button launcher for the entity %s!", 
							moduleData.getName(), ent.getName()));
					addEntity(ent, toolBarEntities);
				} // if entity is visible
				else {
					this.logger.info(String.format("Module %s --> Adding hidden entity %s!", 
							moduleData.getName(), ent.getName()));
				} // else entity is not visible
			} // for each entity
		} // else there are any entity
	}
	
	/**
	 * Get the entities toolbar widget where the buttons for each entity of the
	 * module are placed.
	 * @return SwtWidget, module's entities toolbar.
	 */
	private SwtWidget getEntitiesToolBar() {
		// Get the toolbar to fill it with all the entities defined for the module
		SwtWidget toolBarEntities = this.gui.getSwtWidget(InterfaceExplica.EXPLICA_MODULE_ENTITIES_TOOLBAR_NAME);
		this.logger.debug("Entities toolbar found: " + toolBarEntities);
		
		return toolBarEntities;
	}
	
	
	private EntityLauncherFactory getEntityLauncherFactory(EntityOT entOT) {
		EntityLauncherFactory factory = null;
		if (entOT != null && entOT.getKey() != null && entOT.getKey().length() > 0) {
			factory = ClientContext.getInstance().getEntityLauncherFactory(entOT.getKey());
			if (factory == null ) {
				ClientContext.getInstance().initEntityLauncherFactory(entOT, this.logger);
				factory = ClientContext.getInstance().getEntityLauncherFactory(entOT.getKey());
			}
		}
		
		return factory;
	}
	
	public SwtWidget getModuleContentsPanel() {
		// Get the module contents that will hold the entity view
		return this.gui.getSwtWidget(InterfaceExplica.EXPLICA_ENTITY_PANEL_NAME);
	}
	
	/**
	 * Returns tag Id assigned to CVS source file.
	 */
	public static String getRevision() {
		return "$Id: ExplicaModuleView.java,v 1.9 2009-10-27 10:09:06 mario Exp $";
	}
}
